Skip to content

Conversation

@bramwelt
Copy link
Contributor

@bramwelt bramwelt commented Jan 8, 2026

This pull request adds OpenTelemetry (OTel) tracing support to the lfx-v2-access-check service, enabling distributed tracing and improved observability. The changes include initializing the OTel SDK with configuration from environment variables, instrumenting the HTTP server, and introducing utility code for OTel setup and configuration. Unit tests are also added to ensure correct OTel configuration handling.

OpenTelemetry Integration:

  • Added a new utility (pkg/utils/otel.go) to configure and initialize the OpenTelemetry SDK, supporting both gRPC and HTTP protocols, and allowing configuration via environment variables. This includes helpers for propagators, trace providers, and exporter setup.
  • Updated the main entrypoint (cmd/lfx-access-check/main.go) to initialize the OTel SDK at startup and ensure proper shutdown, using the new utility functions. [1] [2]
  • Instrumented the HTTP server by wrapping the handler with OTel's HTTP middleware (otelhttp.NewHandler), enabling automatic tracing of incoming HTTP requests. [1] [2]

Dependency and Testing Updates:

  • Updated go.mod to add required OpenTelemetry dependencies, update some existing dependencies, and ensure compatibility. [1] [2]
  • Added unit tests for the new OTel utility functions in pkg/utils/otel_test.go, covering environment variable parsing and basic SDK setup/shutdown scenarios.

Copilot AI review requested due to automatic review settings January 8, 2026 21:30
@bramwelt bramwelt requested a review from a team as a code owner January 8, 2026 21:30
@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Initializes OpenTelemetry from environment at startup, instruments the HTTP server, adds a reusable OTEL utility package with tests, updates logging to include trace/span IDs, adds Helm values/env for OTEL, and updates module dependencies and chart version.

Changes

Cohort / File(s) Summary
Application Integration
cmd/lfx-access-check/main.go, cmd/lfx-access-check/server.go
Initialize OTEL SDK using pkg/utils (config from env), derive ctx from OTEL init, defer aggregated shutdown; wrap HTTP handler with otelhttp.NewHandler for automatic request instrumentation.
OTEL Utilities
pkg/utils/otel.go
New public OTEL utilities: OTelConfig + OTelConfigFromEnv, SetupOTelSDK / SetupOTelSDKWithConfig, resource/propagator/traces/metrics/logs provider builders, and phased shutdown aggregation.
OTEL Tests
pkg/utils/otel_test.go
Unit tests for env parsing, defaults, propagator/resource construction, and SDK setup/shutdown (including all-exporters-disabled case).
Logging Integration
pkg/log/log.go, pkg/logging/context.go
Add Otel-aware slog handler, extract trace_id/span_id from context (LogAttrsFromContext, LogWithContext), and chain handler to include context trace attrs in logs.
Helm / Deployment
charts/lfx-v2-access-check/templates/deployment.yaml, charts/lfx-v2-access-check/values.yaml, charts/lfx-v2-access-check/Chart.yaml
Add conditional OTEL env vars and extraEnv support in Deployment template; add app.otel chart values; bump chart version 0.2.5 → 0.2.6.
Dependencies / Build
go.mod
Add/align OpenTelemetry & OTLP dependencies (otelhttp, otlptrace/otlp exporters, sdk, jaeger propagator, etc.), update various indirect module versions.

Sequence Diagram

sequenceDiagram
    participant App as Application (main)
    participant Env as Environment
    participant Utils as pkg/utils
    participant SDK as OpenTelemetry SDK
    participant Server as HTTP Server
    participant OTLP as OTLP Exporter

    App->>Env: Read OTEL_* env vars
    App->>Utils: OTelConfigFromEnv()
    Utils-->>App: OTelConfig
    App->>SDK: SetupOTelSDKWithConfig(ctx, config)
    SDK->>SDK: Install propagators (tracecontext, baggage, ...)
    alt Traces exporter != "none"
        SDK->>OTLP: Create/export via HTTP or gRPC
        OTLP-->>SDK: Exporter client
        SDK->>SDK: Register TracerProvider (and other providers) + Shutdown funcs
    end
    SDK-->>App: shutdown func
    App->>Server: Start HTTP server with OTEL-aware ctx
    Server->>Server: Wrap handlers with otelhttp.NewHandler
    Server-->>Client: Serve instrumented requests
    App->>App: Defer aggregated shutdown on exit
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding OpenTelemetry tracing support to the service.
Description check ✅ Passed The description comprehensively explains the OpenTelemetry integration, including SDK initialization, HTTP instrumentation, and dependency updates.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from LFXV2-612: adds OTel dependencies, configures initialization with environment variables, instruments HTTP requests, and enables trace propagation.
Out of Scope Changes check ✅ Passed All changes are aligned with LFXV2-612 objectives. The chart version bump (0.2.5 to 0.2.6) is a reasonable operational change for releasing OTel-enabled configuration.
Docstring Coverage ✅ Passed Docstring coverage is 90.48% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bramwelt/otel-implementation

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
pkg/utils/otel.go (2)

95-98: Minor formatting and potential concern with handleErr pattern.

There's a missing space after the comma on line 97. Additionally, this handleErr function is defined but only used when newTraceProvider fails. The pattern is correct for cleanup, but ensure the named return err is intended to be set via closure side-effect.

🔧 Suggested formatting fix
 	handleErr := func(inErr error) {
-		err = errors.Join(inErr,shutdown(ctx))
+		err = errors.Join(inErr, shutdown(ctx))
 	}

128-144: Consider validating the protocol value.

Unknown protocol values (anything other than "http") silently default to gRPC. This could cause confusion if a user misconfigures OTEL_EXPORTER_OTLP_PROTOCOL with an invalid value like "https" or a typo.

Consider either:

  1. Logging when falling back to gRPC
  2. Validating and returning an error for unsupported protocols
♻️ Option: Add explicit protocol validation
 func newTraceProvider(ctx context.Context, config OTelConfig) (*trace.TracerProvider, error) {
 	var traceClient otlptrace.Client
 
-	if config.Protocol == "http" {
+	switch config.Protocol {
+	case "http":
 		opts := []otlptracehttp.Option{
 			otlptracehttp.WithEndpoint(config.Endpoint),
 		}
 		if config.InsecureExporter {
 			opts = append(opts, otlptracehttp.WithInsecure())
 		}
 		traceClient = otlptracehttp.NewClient(opts...)
-	} else {
+	case "grpc", "":
 		opts := []otlptracegrpc.Option{
 			otlptracegrpc.WithEndpoint(config.Endpoint),
 		}
 		if config.InsecureExporter {
 			opts = append(opts, otlptracegrpc.WithInsecure())
 		}
 		traceClient = otlptracegrpc.NewClient(opts...)
+	default:
+		return nil, fmt.Errorf("unsupported OTLP protocol: %s (expected 'grpc' or 'http')", config.Protocol)
 	}
pkg/utils/otel_test.go (2)

62-69: Consider using t.Setenv for automatic cleanup.

The current approach of manually calling os.Unsetenv and os.Setenv doesn't restore the original environment after the test completes. Using t.Setenv (available since Go 1.17) automatically restores the original value when the test completes, providing better isolation.

♻️ Suggested improvement
 		t.Run(tt.name, func(t *testing.T) {
-			// Clear relevant env vars
-			envKeys := []string{
-				"OTEL_SERVICE_NAME",
-				"OTEL_SERVICE_VERSION",
-				"OTEL_EXPORTER_OTLP_ENDPOINT",
-				"OTEL_EXPORTER_OTLP_PROTOCOL",
-				"OTEL_TRACES_EXPORTER",
-				"OTEL_EXPORTER_OTLP_INSECURE",
-			}
-			for _, key := range envKeys {
-				os.Unsetenv(key)
-			}
-
-			// Set test env vars
+			// Set test env vars (t.Setenv auto-restores after test)
 			for key, value := range tt.envVars {
-				os.Setenv(key, value)
+				t.Setenv(key, value)
 			}

Note: For default values test case, you may need to explicitly set empty values or use a helper to ensure vars are unset.


117-135: Test for SetupOTelSDK should ensure complete environment isolation.

This test only unsets OTEL_TRACES_EXPORTER but other OTEL environment variables from previous tests or the system environment could still affect the test. Consider clearing all relevant environment variables for consistent test behavior.

♻️ Suggested improvement
 func TestSetupOTelSDK(t *testing.T) {
-	// Clear env vars to use defaults
-	os.Unsetenv("OTEL_TRACES_EXPORTER")
+	// Clear all OTEL env vars to ensure defaults are used
+	for _, key := range []string{
+		"OTEL_SERVICE_NAME",
+		"OTEL_SERVICE_VERSION",
+		"OTEL_EXPORTER_OTLP_ENDPOINT",
+		"OTEL_EXPORTER_OTLP_PROTOCOL",
+		"OTEL_TRACES_EXPORTER",
+		"OTEL_EXPORTER_OTLP_INSECURE",
+	} {
+		t.Setenv(key, "")
+	}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8943c53 and bec399b.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (5)
  • cmd/lfx-access-check/main.go
  • cmd/lfx-access-check/server.go
  • go.mod
  • pkg/utils/otel.go
  • pkg/utils/otel_test.go
🧰 Additional context used
📓 Path-based instructions (4)
pkg/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

All public packages should be placed under the pkg directory

Files:

  • pkg/utils/otel_test.go
  • pkg/utils/otel.go
cmd/lfx-access-check/{main.go,server.go}

📄 CodeRabbit inference engine (CLAUDE.md)

All requests require valid JWT tokens for authentication

Files:

  • cmd/lfx-access-check/server.go
  • cmd/lfx-access-check/main.go
cmd/lfx-access-check/server.go

📄 CodeRabbit inference engine (CLAUDE.md)

All API endpoints must implement liveness and readiness probes at /livez and /readyz

Files:

  • cmd/lfx-access-check/server.go
{cmd/lfx-access-check/server.go,internal/middleware/*.go}

📄 CodeRabbit inference engine (CLAUDE.md)

All requests must be tracked with unique request IDs for correlation in logs

Files:

  • cmd/lfx-access-check/server.go
🧬 Code graph analysis (2)
pkg/utils/otel_test.go (1)
pkg/utils/otel.go (4)
  • OTelConfig (23-30)
  • OTelConfigFromEnv (34-70)
  • SetupOTelSDKWithConfig (80-116)
  • SetupOTelSDK (74-76)
cmd/lfx-access-check/main.go (1)
pkg/utils/otel.go (2)
  • OTelConfigFromEnv (34-70)
  • SetupOTelSDKWithConfig (80-116)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: CodeQL analysis (go)
  • GitHub Check: Agent
  • GitHub Check: MegaLinter
🔇 Additional comments (5)
cmd/lfx-access-check/server.go (1)

101-103: LGTM! OTel HTTP instrumentation is correctly applied.

The otelhttp.NewHandler wrapper is placed as the outermost layer in the middleware stack, which ensures all incoming requests are traced. The operation name "access-check" provides a reasonable default span name. Request ID middleware is correctly applied before tracing, so trace context will include request correlation.

cmd/lfx-access-check/main.go (1)

28-40: LGTM! OTel SDK initialization follows best practices.

The initialization sequence correctly:

  1. Creates config from environment variables
  2. Sets up the SDK early in the application lifecycle
  3. Defers shutdown with a fresh context.Background() (appropriate since the original context may be cancelled during shutdown)
  4. Exits on setup failure with proper error logging
pkg/utils/otel.go (1)

151-169: LGTM! Resource and TracerProvider configuration follows best practices.

The implementation correctly:

  • Merges default resource attributes with custom service name/version
  • Uses semantic conventions (semconv v1.26.0)
  • Configures a batched exporter with a reasonable 5-second timeout
pkg/utils/otel_test.go (1)

95-115: LGTM! Basic SDK setup test covers the no-exporter path.

The test validates that when TracesExporter is set to "none", the SDK initializes successfully and the shutdown function works correctly.

go.mod (1)

11-15: OpenTelemetry version mismatch may cause compatibility issues.

The OTel dependencies have inconsistent versions:

  • otel/sdk v1.39.0
  • otel v1.39.0
  • otlptrace v1.36.0
  • otlptracegrpc v1.35.0
  • otlptracehttp v1.35.0

OpenTelemetry documentation explicitly recommends keeping exporters and the SDK on the same major/minor release to avoid incompatibilities. The current state pairs SDK v1.39 with exporters at v1.35-1.36. Consider aligning all go.opentelemetry.io/otel/exporters/otlp/otlptrace/* packages to v1.39.0 to match the SDK version.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds comprehensive OpenTelemetry (OTel) tracing support to the lfx-v2-access-check service. The implementation includes SDK configuration from environment variables, automatic HTTP request instrumentation, and proper lifecycle management for the tracing infrastructure.

Key Changes:

  • Introduced new OTel utility package with environment-based configuration and SDK setup functions
  • Integrated OTel initialization in the main entrypoint with proper shutdown handling
  • Instrumented HTTP server with OTel middleware for automatic request tracing

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
pkg/utils/otel.go New utility for OTel SDK configuration and initialization with support for gRPC/HTTP protocols
pkg/utils/otel_test.go Unit tests for OTel configuration parsing and SDK setup/shutdown
cmd/lfx-access-check/main.go Added OTel SDK initialization and shutdown hooks at service startup
cmd/lfx-access-check/server.go Wrapped HTTP handler with OTel instrumentation middleware
go.mod Added OpenTelemetry dependencies and updated related packages
go.sum Updated checksums for new and modified dependencies

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

ServiceName string
ServiceVersion string
Endpoint string
Protocol string // "grpc" or "http"
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Protocol field accepts any string value without validation. If a value other than "grpc" or "http" is provided (line 50-53 in otel.go), the code will default to gRPC behavior (line 136-143), but this could be confusing for users. Consider adding validation to return an error for invalid protocol values, or at least document the default behavior in the OTelConfig struct comments.

Suggested change
Protocol string // "grpc" or "http"
Protocol string // "grpc" (default) or "http"; any value other than "http" is treated as "grpc"

Copilot uses AI. Check for mistakes.
Comment on lines 12 to 93
func TestOTelConfigFromEnv(t *testing.T) {
tests := []struct {
name string
envVars map[string]string
expected OTelConfig
}{
{
name: "default values",
envVars: map[string]string{},
expected: OTelConfig{
ServiceName: "lfx-v2-access-check",
ServiceVersion: "1.0.0",
Endpoint: "localhost:4317",
Protocol: "grpc",
TracesExporter: "none",
InsecureExporter: false,
},
},
{
name: "custom values",
envVars: map[string]string{
"OTEL_SERVICE_NAME": "custom-service",
"OTEL_SERVICE_VERSION": "2.0.0",
"OTEL_EXPORTER_OTLP_ENDPOINT": "otel-collector:4317",
"OTEL_EXPORTER_OTLP_PROTOCOL": "http",
"OTEL_TRACES_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_INSECURE": "true",
},
expected: OTelConfig{
ServiceName: "custom-service",
ServiceVersion: "2.0.0",
Endpoint: "otel-collector:4317",
Protocol: "http",
TracesExporter: "otlp",
InsecureExporter: true,
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Clear relevant env vars
envKeys := []string{
"OTEL_SERVICE_NAME",
"OTEL_SERVICE_VERSION",
"OTEL_EXPORTER_OTLP_ENDPOINT",
"OTEL_EXPORTER_OTLP_PROTOCOL",
"OTEL_TRACES_EXPORTER",
"OTEL_EXPORTER_OTLP_INSECURE",
}
for _, key := range envKeys {
os.Unsetenv(key)
}

// Set test env vars
for key, value := range tt.envVars {
os.Setenv(key, value)
}

config := OTelConfigFromEnv()

if config.ServiceName != tt.expected.ServiceName {
t.Errorf("ServiceName = %v, want %v", config.ServiceName, tt.expected.ServiceName)
}
if config.ServiceVersion != tt.expected.ServiceVersion {
t.Errorf("ServiceVersion = %v, want %v", config.ServiceVersion, tt.expected.ServiceVersion)
}
if config.Endpoint != tt.expected.Endpoint {
t.Errorf("Endpoint = %v, want %v", config.Endpoint, tt.expected.Endpoint)
}
if config.Protocol != tt.expected.Protocol {
t.Errorf("Protocol = %v, want %v", config.Protocol, tt.expected.Protocol)
}
if config.TracesExporter != tt.expected.TracesExporter {
t.Errorf("TracesExporter = %v, want %v", config.TracesExporter, tt.expected.TracesExporter)
}
if config.InsecureExporter != tt.expected.InsecureExporter {
t.Errorf("InsecureExporter = %v, want %v", config.InsecureExporter, tt.expected.InsecureExporter)
}
})
}
}
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test suite doesn't cover the scenario where an invalid protocol value is provided (neither "grpc" nor "http"). While the implementation defaults to gRPC in this case (line 136-143 in otel.go), adding a test case would document this behavior and prevent regressions.

Copilot uses AI. Check for mistakes.
go.mod Outdated
Comment on lines 12 to 14
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OpenTelemetry dependency versions are inconsistent across the module. The core SDK and trace packages are at version 1.39.0, while the otlptrace exporters are at mixed versions (v1.36.0 for the base package and v1.35.0 for the gRPC and HTTP variants). This version mismatch could lead to compatibility issues or unexpected behavior. All OpenTelemetry packages from the same release should typically use the same version to ensure compatibility.

Suggested change
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0

Copilot uses AI. Check for mistakes.
Comment on lines 53 to 69
// Clear relevant env vars
envKeys := []string{
"OTEL_SERVICE_NAME",
"OTEL_SERVICE_VERSION",
"OTEL_EXPORTER_OTLP_ENDPOINT",
"OTEL_EXPORTER_OTLP_PROTOCOL",
"OTEL_TRACES_EXPORTER",
"OTEL_EXPORTER_OTLP_INSECURE",
}
for _, key := range envKeys {
os.Unsetenv(key)
}

// Set test env vars
for key, value := range tt.envVars {
os.Setenv(key, value)
}
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test manipulates global environment variables using os.Setenv and os.Unsetenv without proper cleanup, which can cause test pollution and race conditions when tests run in parallel. Consider using t.Setenv() instead, which was introduced in Go 1.17 and automatically handles cleanup and prevents parallel execution of tests that modify the same environment variable.

Copilot uses AI. Check for mistakes.
Comment on lines 118 to 119
// Clear env vars to use defaults
os.Unsetenv("OTEL_TRACES_EXPORTER")
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test modifies global environment variables without proper cleanup. Similar to TestOTelConfigFromEnv, consider using t.Setenv() instead of os.Unsetenv() for automatic cleanup and better test isolation.

Suggested change
// Clear env vars to use defaults
os.Unsetenv("OTEL_TRACES_EXPORTER")
// Clear env vars to use defaults; t.Setenv ensures cleanup after the test
t.Setenv("OTEL_TRACES_EXPORTER", "")

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +40
defer func() {
if shutdownErr := otelShutdown(context.Background()); shutdownErr != nil {
slog.Error("error shutting down OpenTelemetry SDK", "error", shutdownErr)
}
}()
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OTel shutdown is deferred to use a new context.Background() instead of the ctx variable that was used for initialization. This means any shutdown timeout or cancellation in the main context won't be respected during shutdown. Consider creating a separate shutdown context with a timeout (e.g., context.WithTimeout) to allow graceful shutdown with a reasonable deadline.

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
charts/lfx-v2-access-check/values.yaml (1)

54-73: otel.insecure is a string; Helm if treats "false" as true—align value type with intended rendering.

Right now insecure: "false" will still satisfy if .Values.otel.insecure (non-empty), so the deployment will emit OTEL_EXPORTER_OTLP_INSECURE even when “false”. If you intended “only emit when true”, make it a boolean.

Proposed patch (use boolean in values)
-  insecure: "false"
+  insecure: false
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bec399b and 459d35d.

📒 Files selected for processing (2)
  • charts/lfx-v2-access-check/templates/deployment.yaml
  • charts/lfx-v2-access-check/values.yaml
🧰 Additional context used
📓 Path-based instructions (1)
charts/lfx-v2-access-check/**

📄 CodeRabbit inference engine (CLAUDE.md)

Helm charts for Kubernetes deployment must be placed in the charts/lfx-v2-access-check directory

Files:

  • charts/lfx-v2-access-check/values.yaml
  • charts/lfx-v2-access-check/templates/deployment.yaml
🪛 YAMLlint (1.37.1)
charts/lfx-v2-access-check/templates/deployment.yaml

[error] 42-42: syntax error: could not find expected ':'

(syntax)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: MegaLinter
🔇 Additional comments (1)
charts/lfx-v2-access-check/templates/deployment.yaml (1)

41-64: The Helm templates in charts/*/templates/** are explicitly ignored by YAMLlint (see .yamllint.yml line 52), so this file will not be linted. Additionally, YAML_YAMLLINT is disabled in MegaLinter configuration. The proposed patch—converting {{- if ... }} to YAML comments—would break Helm's template conditional logic and cause all environment variables to render regardless of their conditions.

Likely an incorrect or invalid review comment.

Integrate slog-otel to automatically include trace_id and span_id
from OpenTelemetry context in log output, enabling correlation
between logs and distributed traces.

Changes:
- pkg/log/log.go: Wrap JSON handler with slogotel.OtelHandler
- pkg/utils/otel_test.go: Fix TestNewPropagator to pass OTelConfig
- charts/: Bump version to 0.2.6
- go.mod: Add github.com/remychantenay/slog-otel v1.3.4

Issue: LFXV2-612

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Signed-off-by: Trevor Bramwell <[email protected]>
@bramwelt bramwelt force-pushed the bramwelt/otel-implementation branch from 459d35d to bec5ace Compare January 14, 2026 15:12
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@pkg/utils/otel.go`:
- Around line 136-147: The current call to slog.With(...).Debug("OTelConfig")
creates a new *slog.Logger but never uses the returned logger, so the debug log
is not emitted; fix it by capturing the returned logger from slog.With (e.g.,
assign to a variable like l or logger) and then call logger.Debug("OTelConfig")
on that instance (or alternatively call slog.Debug with the key/value pairs),
ensuring you reference the same key-value fields used in the original slog.With
invocation.
♻️ Duplicate comments (1)
pkg/utils/otel.go (1)

44-80: LGTM!

The OTelConfig struct is well-documented with clear environment variable mappings. The Protocol field validation concern has been previously raised.

🧹 Nitpick comments (3)
pkg/logging/context.go (1)

15-37: LGTM! Useful helpers for manual trace context injection.

These complement the automatic OtelHandler integration in pkg/log/log.go by providing explicit control for ad-hoc logger enrichment scenarios.

💡 Optional: Reduce duplication between the two functions
 func LogAttrsFromContext(ctx context.Context) []slog.Attr {
 	spanCtx := trace.SpanContextFromContext(ctx)
 	if !spanCtx.IsValid() {
 		return nil
 	}
 	return []slog.Attr{
 		slog.String("trace_id", spanCtx.TraceID().String()),
 		slog.String("span_id", spanCtx.SpanID().String()),
 	}
 }
 
 func LogWithContext(ctx context.Context, logger *slog.Logger) *slog.Logger {
-	spanCtx := trace.SpanContextFromContext(ctx)
-	if !spanCtx.IsValid() {
+	attrs := LogAttrsFromContext(ctx)
+	if attrs == nil {
 		return logger
 	}
-	return logger.With(
-		slog.String("trace_id", spanCtx.TraceID().String()),
-		slog.String("span_id", spanCtx.SpanID().String()),
-	)
+	args := make([]any, len(attrs))
+	for i, attr := range attrs {
+		args[i] = attr
+	}
+	return logger.With(args...)
 }
cmd/lfx-access-check/main.go (1)

28-40: Consider adding a timeout to the OTel shutdown context.

The OTel initialization and error handling are correct. Using context.Background() for shutdown is reasonable since the main context may already be cancelled. However, adding a timeout ensures the shutdown doesn't hang indefinitely if the OTLP endpoint is unresponsive.

⏱️ Suggested timeout for graceful shutdown
 defer func() {
-	if shutdownErr := otelShutdown(context.Background()); shutdownErr != nil {
+	shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer shutdownCancel()
+	if shutdownErr := otelShutdown(shutdownCtx); shutdownErr != nil {
 		slog.Error("error shutting down OpenTelemetry SDK", "error", shutdownErr)
 	}
 }()

You'll need to add "time" to the imports.

pkg/utils/otel.go (1)

356-390: LGTM!

Logger provider setup is consistent with the other providers.

Optional: The exporter configuration logic (endpoint and insecure options) is duplicated across newTraceProvider, newMetricsProvider, and newLoggerProvider. Consider extracting a helper if this pattern expands, but the current explicit approach is clear and maintainable.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 459d35d and bec5ace.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (10)
  • charts/lfx-v2-access-check/Chart.yaml
  • charts/lfx-v2-access-check/templates/deployment.yaml
  • charts/lfx-v2-access-check/values.yaml
  • cmd/lfx-access-check/main.go
  • cmd/lfx-access-check/server.go
  • go.mod
  • pkg/log/log.go
  • pkg/logging/context.go
  • pkg/utils/otel.go
  • pkg/utils/otel_test.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • pkg/utils/otel_test.go
  • cmd/lfx-access-check/server.go
  • go.mod
  • charts/lfx-v2-access-check/values.yaml
🧰 Additional context used
📓 Path-based instructions (4)
pkg/log/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

pkg/log/*.go: Use structured logging in JSON format with consistent fields
Log levels must be used consistently: DEBUG, INFO, WARN, ERROR

Files:

  • pkg/log/log.go
pkg/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

All public packages should be placed under the pkg directory

Files:

  • pkg/log/log.go
  • pkg/logging/context.go
  • pkg/utils/otel.go
cmd/lfx-access-check/{main.go,server.go}

📄 CodeRabbit inference engine (CLAUDE.md)

All requests require valid JWT tokens for authentication

Files:

  • cmd/lfx-access-check/main.go
charts/lfx-v2-access-check/**

📄 CodeRabbit inference engine (CLAUDE.md)

Helm charts for Kubernetes deployment must be placed in the charts/lfx-v2-access-check directory

Files:

  • charts/lfx-v2-access-check/Chart.yaml
  • charts/lfx-v2-access-check/templates/deployment.yaml
🧬 Code graph analysis (1)
cmd/lfx-access-check/main.go (1)
pkg/utils/otel.go (2)
  • OTelConfigFromEnv (84-161)
  • SetupOTelSDKWithConfig (171-239)
🪛 YAMLlint (1.37.1)
charts/lfx-v2-access-check/templates/deployment.yaml

[error] 42-42: syntax error: could not find expected ':'

(syntax)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: MegaLinter
🔇 Additional comments (13)
pkg/log/log.go (1)

105-111: LGTM! Clean OTel integration with proper handler chaining.

The handler chain correctly wraps the JSON handler with slog-otel for trace/span ID injection, then with contextHandler for context-based attributes. This preserves structured JSON logging while adding observability.

charts/lfx-v2-access-check/templates/deployment.yaml (2)

41-43: LGTM! Standard Helm pattern for extra environment variables.

The toYaml with nindent 12 is the correct approach for rendering additional environment variables. The YAMLlint error on line 42 is a false positive—this is valid Helm templating syntax that gets processed before YAML validation.


44-83: LGTM! OpenTelemetry environment variables are correctly configured.

All environment variable names align with what OTelConfigFromEnv() in pkg/utils/otel.go expects. The conditional rendering and quoting are appropriate.

charts/lfx-v2-access-check/Chart.yaml (1)

8-8: LGTM! Version bump reflects the new OpenTelemetry configuration.

cmd/lfx-access-check/main.go (1)

46-53: LGTM! Signal handling with cancellable context is well-structured.

The context derivation chain and signal handling for graceful shutdown are correctly implemented.

pkg/utils/otel.go (8)

1-30: LGTM!

Package declaration, copyright header, and imports are well-organized. The OpenTelemetry SDK imports are appropriate for the functionality being implemented.


32-42: LGTM!

Constants are well-defined with clear documentation.


163-167: LGTM!

Clean wrapper function that allows for both environment-based and programmatic configuration, improving testability.


169-239: LGTM!

The SDK setup function is well-structured with proper error aggregation via errors.Join, idempotent shutdown handling, and conditional provider initialization. The pattern of registering shutdown functions and setting global providers follows OpenTelemetry best practices.


241-251: LGTM!

Resource creation correctly merges default attributes with service-specific ones using semantic conventions.


253-277: LGTM!

Good defensive implementation with proper fallback to default propagators and warning logging for unknown values.


279-316: LGTM!

Trace provider setup correctly configures the OTLP exporter with protocol-specific options and uses TraceIDRatioBased sampler for configurable sampling. The batch processor with 1-second timeout is a reasonable default.


318-354: LGTM!

Metrics provider follows the same pattern as the trace provider. The 30-second periodic reader interval is a reasonable default for metrics export.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +136 to +147
slog.With(
"service-name", serviceName,
"version", serviceVersion,
"protocol", protocol,
"endpoint", endpoint,
"insecure", insecure,
"traces-exporter", tracesExporter,
"traces-sample-ratio", tracesSampleRatio,
"metrics-exporter", metricsExporter,
"logs-exporter", logsExporter,
"propagators", propagators,
).Debug("OTelConfig")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix incorrect slog usage — debug log is never emitted.

slog.With(...) returns a new *slog.Logger instance but does not modify the default logger. Calling .Debug() on the returned logger without storing or using it elsewhere means this log statement has no effect.

🐛 Proposed fix
-	slog.With(
+	slog.Debug("OTelConfig",
 		"service-name", serviceName,
 		"version", serviceVersion,
 		"protocol", protocol,
 		"endpoint", endpoint,
 		"insecure", insecure,
 		"traces-exporter", tracesExporter,
 		"traces-sample-ratio", tracesSampleRatio,
 		"metrics-exporter", metricsExporter,
 		"logs-exporter", logsExporter,
 		"propagators", propagators,
-	).Debug("OTelConfig")
+	)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
slog.With(
"service-name", serviceName,
"version", serviceVersion,
"protocol", protocol,
"endpoint", endpoint,
"insecure", insecure,
"traces-exporter", tracesExporter,
"traces-sample-ratio", tracesSampleRatio,
"metrics-exporter", metricsExporter,
"logs-exporter", logsExporter,
"propagators", propagators,
).Debug("OTelConfig")
slog.Debug("OTelConfig",
"service-name", serviceName,
"version", serviceVersion,
"protocol", protocol,
"endpoint", endpoint,
"insecure", insecure,
"traces-exporter", tracesExporter,
"traces-sample-ratio", tracesSampleRatio,
"metrics-exporter", metricsExporter,
"logs-exporter", logsExporter,
"propagators", propagators,
)
🤖 Prompt for AI Agents
In `@pkg/utils/otel.go` around lines 136 - 147, The current call to
slog.With(...).Debug("OTelConfig") creates a new *slog.Logger but never uses the
returned logger, so the debug log is not emitted; fix it by capturing the
returned logger from slog.With (e.g., assign to a variable like l or logger) and
then call logger.Debug("OTelConfig") on that instance (or alternatively call
slog.Debug with the key/value pairs), ensuring you reference the same key-value
fields used in the original slog.With invocation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants